const defaultConfig = {
  HttpPort: 76,
  // Log to file
  LogToFile: true,
  matchmakers: ['http://localhost:90'], // 默认值
};

const process = require('process');
const express = require('express');
const axios = require('axios');
const cors = require('cors');
const argv = require('yargs').argv;
const { Mutex } = require('async-mutex');

var configFile = typeof argv.configFile != 'undefined' ? argv.configFile.toString() : 'config.json';
const config = require('./modules/config.js').init(configFile, defaultConfig);

const app = express();
app.use(express.json());
app.use(cors()); //全局启用CORS

const logging = require('./modules/logging.js');
logging.RegisterConsoleLogger();

if (config.LogToFile) {
  logging.RegisterFileLogger('./logs');
}

console.log(`configFile ${configFile}`);
console.log('Config: ' + JSON.stringify(config, null, '\t'));

// 从配置文件中读取 Matchmaker 节点列表
const matchmakers = config.matchmakers;

// 一个用于跟踪matchmaker节点使用计数的Map，key为matchmaker节点的URL，value为使用计数, 统计最近一段时间内被getBestMatchmaker分配的次数
const recentlyDispatchMatchmakerCountMap = new Map();

// 一个锁来保护对recentlyDispatchMatchmakerCountMap的并发访问
const dispatchMatchmakerCountMutex = new Mutex();

// 初始化所有matchmaker节点的计数为0
matchmakers.forEach((url) => {
  recentlyDispatchMatchmakerCountMap.set(url, 0);
});

//从配置文件中读取文件服务的基础URL
const fileServiceBaseUrl = config.fileServiceBaseUrl;

//
// Parse command line.
//
if (typeof argv.HttpPort != 'undefined') {
  config.HttpPort = argv.HttpPort;
}

// 日志记录请求信息
function logRequest(req, label = '请求') {
  const protocol =
    req.headers['x-forwarded-proto'] || (req.connection.encrypted ? 'https' : 'http');
  const host = req.headers.host || 'localhost';
  const fullUrl = `${protocol}://${host}${req.originalUrl}`;
  console.log(`[${label}] 方法: ${req.method}, URL: ${fullUrl}`);
  //   console.log(`[Headers]`, req.headers);
  //   console.log(`[Query]`, req.query);
  console.log(`[Body]`, req.body);
}

// 获取所有 matchmaker 的负载，返回负载最小的节点
async function getBestMatchmaker() {
  console.log('获取所有matchmaker实例统计信息, 查找最佳节点...');
  let best = null;
  let minLoad = Infinity;

  // 并发执行所有请求
  const loadPromises = matchmakers.map(async (url) => {
    try {
      const loadRes = await axios.get(url + '/v1/server/status', { timeout: 5000 });
      return { url, data: loadRes.data, error: null };
    } catch (e) {
      console.log(`无法获取${url}实例统计信息: ${e.message}`);
      return { url, data: null, error: e.message };
    }
  });

  // 等待所有请求完成
  const results = await Promise.all(loadPromises);

  // 处理结果，找出最佳节点
  await dispatchMatchmakerCountMutex.runExclusive(async () => {
    for (const result of results) {
      if (result.error) continue;

      const { runningInstances, pendingInstances, maxInstances } = result.data;

      // 排除已达最大实例数的节点
      const totalInstances = runningInstances + pendingInstances;
      if (totalInstances >= maxInstances) {
        // console.log(
        //   `Matchmaker ${result.url} 活跃实例数为${runningInstances}，待启动实例数为${pendingInstances}，已达最大实例数(${maxInstances})，跳过`,
        // );
        continue;
      }

      // 考虑最近一段时间内被getBestMatchmaker分配的次数，选择使用次数最少的节点
      const currentNodeCount = recentlyDispatchMatchmakerCountMap.get(result.url) || 0;
      const combinedInstancesCount = totalInstances + currentNodeCount;
      if (combinedInstancesCount < minLoad) {
        minLoad = combinedInstancesCount;
        best = result.url;
      }
    }
    if (best) {
      //找到最佳节点，增加计数
      recentlyDispatchMatchmakerCountMap.set(
        best,
        (recentlyDispatchMatchmakerCountMap.get(best) || 0) + 1,
      );
      // 一段时间后减少计数
      setTimeout(async () => {
        await dispatchMatchmakerCountMutex.runExclusive(() => {
          const count = recentlyDispatchMatchmakerCountMap.get(best) || 0;
          if (count > 0) {
            recentlyDispatchMatchmakerCountMap.set(best, count - 1);
            // console.log(`节点 ${best} 使用计数减少到 ${count - 1}`);
          }
        });
      }, 5000);
    }
  });
  return best;
}

// 请求文件服务，获取模型文件路径
async function getFilePathFromFileService(fileId, userName, fawkesAuthToken) {
  const filePathUrl = `${fileServiceBaseUrl}/permissionAndFilePath?targetId=${fileId}&userName=${userName}`;
  try {
    const fileRes = await axios.get(filePathUrl, {
      timeout: 10000,
      headers: {
        'fawkes-auth': fawkesAuthToken,
      },
    });
    console.log(`请求文件服务 URL: ${filePathUrl}, 请求方式: GET`);
    console.log(`请求文件服务的请求头:`, fileRes.config.headers);

    if (fileRes.data && fileRes.data.status) {
      console.log(`请求文件服务成功，返回内容:`, fileRes.data);
      console.log(`请求文件服务成功，文件路径:`, fileRes.data.data.filePath);
      return fileRes.data.data.filePath;
    }
    console.log(`获取文件路径失败:`, fileRes.data);
  } catch (e) {
    console.log(`请求文件服务失败响应数据:`, e.response?.data);
  }
  return '';
}

// 智能调度启动实例
app.post('/v1/instances/start', async (req, res) => {
  const { filePath = '', fileId = '', userName = '', fawkesAuthToken = '' } = req.body || {};

  logRequest(req, 'REST请求');
  const best = await getBestMatchmaker();
  if (!best) {
    const errorMessage = `无可用matchmaker节点`;
    console.log(errorMessage);
    return res.status(500).json({ error: errorMessage });
  }

  let modelFilePath =
    filePath || (await getFilePathFromFileService(fileId, userName, fawkesAuthToken));

  try {
    // 转发请求到最佳节点
    console.log(`请求服务器${best}启动新实例`);
    const result = await axios.post(
      best + '/v1/instances/start',
      { modelFilePath: modelFilePath },
      { timeout: 90000 },
    );
    console.log(`服务器${best}返回数据:`, result.data);
    res.json(result.data);
  } catch (e) {
    const errorMessage = `启动实例失败，最佳服务器：${best}, 错误信息: ${e.message}`;
    console.log(errorMessage);
    res.status(500).json({ error: errorMessage });
  }
});

// const responseQueue = [];
// let isProcessingResponses = false;

// async function processResponseInOrder(promise, res, best) {
//   responseQueue.push({ promise, res, best });

//   if (!isProcessingResponses) {
//     processResponses();
//   }
// }

// async function processResponses() {
//   isProcessingResponses = true;

//   while (responseQueue.length > 0) {
//     const { promise, res, best } = responseQueue.shift();
//     try {
//       const result = await promise;
//       console.log(`服务器${best}返回数据:`, result.data);
//       res.json(result.data);
//     } catch (e) {
//       const errorMessage = `启动实例失败，最佳服务器：${best}, 错误信息: ${e.message}`;
//       console.log(errorMessage);
//       res.status(500).json({ error: errorMessage });
//     }
//   }

//   isProcessingResponses = false;
// }

// 关闭指定服务器上的所有实例
app.post('/v1/servers/instances/stop-all', async (req, res) => {
  logRequest(req, 'REST请求');
  const { serverUrl = '' } = req.body || {};
  if (!serverUrl || !matchmakers.includes(serverUrl)) {
    const errorMessage = `无效或不支持的服务器地址`;
    console.log(errorMessage);
    return res.status(400).json({ error: errorMessage });
  }

  try {
    console.log(`请求服务器${serverUrl}关闭所有实例`);
    const result = await axios.post(serverUrl + '/v1/instances/stop-all', {}, { timeout: 5000 });
    console.log(`服务器${serverUrl}返回数据:`, result.data);
    res.json(result.data);
  } catch (e) {
    const errorMessage = `关闭失败，服务器：${serverUrl}, 错误信息: ${e.message}`;
    console.log(errorMessage);
    res.status(500).json({ success: false, error: errorMessage });
  }
});

// 关闭指定服务器上的指定实例
app.post('/v1/servers/instances/stop', async (req, res) => {
  logRequest(req, 'REST请求');
  const { serverUrl = '', instanceId = '' } = req.body || {};
  if (!serverUrl || !instanceId || !matchmakers.includes(serverUrl)) {
    const errorMessage = `无效的服务器地址或实例ID`;
    console.log(errorMessage);
    return res.status(400).json({ error: errorMessage });
  }
  try {
    console.log(`请求服务器${serverUrl}关闭实例${instanceId}`);
    const result = await axios.post(
      `${serverUrl}/v1/instances/stop`,
      { id: instanceId },
      { timeout: 5000 },
    );
    console.log(`服务器${serverUrl}返回数据:`, result.data);
    res.json(result.data);
  } catch (e) {
    const errorMessage = `关闭失败，服务器：${serverUrl}，实例ID: ${instanceId}, 错误信息: ${e.message}`;
    console.log(errorMessage);
    res.status(500).json({ error: errorMessage });
  }
});

//关闭所有服务器上的所有实例
app.post('/v1/instances/stop-all', async (req, res) => {
  logRequest(req, 'REST请求');
  const stopPromises = matchmakers.map(async (url) => {
    try {
      console.log(`请求服务器${url}关闭所有实例`);
      const response = await axios.post(url + '/v1/instances/stop-all', {}, { timeout: 5000 });
      console.log(`服务器${url}返回数据:`, response.data);
      return response.data;
    } catch (e) {
      console.log(`请求服务器${url}关闭所有实例失败:`, e.message);
      return {
        serverUrl: url,
        error: e.message,
      };
    }
  });

  // 等待所有请求完成
  const results = await Promise.all(stopPromises);
  res.json(results);
});

// 获取指定服务器上的所有实例列表
app.post('/v1/servers/instances/list', async (req, res) => {
  logRequest(req, 'REST请求');
  const { serverUrl = '' } = req.body || {};
  if (!serverUrl || !matchmakers.includes(serverUrl)) {
    const errorMessage = '无效或不支持的服务器地址';
    console.log(errorMessage);
    return res.status(400).json({ success: false, error: errorMessage });
  }
  try {
    console.log(`请求获取服务器${serverUrl}上的实例列表`);
    const result = await axios.get(`${serverUrl}/v1/instances/list`, { timeout: 5000 });
    console.log(`服务器${serverUrl}返回数据:`, result.data);
    res.json(result.data);
  } catch (e) {
    const errorMessage = `获取实例列表失败: ${serverUrl}, 错误: ${e.message}`;
    console.log(errorMessage);
    // 当获取指定服务器上的实例列表失败时，设置 HTTP 响应状态码为 500（服务器内部错误）
    res.status(500).json({
      serverUrl,
      error: errorMessage,
    });
  }
});

//获取所有节点负载信息
app.get('/v1/servers/loads', async (req, res) => {
  logRequest(req, 'REST请求');
  const loadPromises = matchmakers.map(async (url) => {
    try {
      console.log(`请求获取服务器${url}的负载信息`);
      const response = await axios.get(url + '/v1/server/load', { timeout: 30000 });
      console.log(`服务器${url}返回数据:`, response.data);
      return response.data;
    } catch (e) {
      const errorMessage = `无法获取${url}负载信息: ${e.message}`;
      console.log(errorMessage);
      return {
        serverUrl: url,
        error: errorMessage,
      };
    }
  });

  // 等待所有请求完成（无论成功还是失败）
  const loads = await Promise.all(loadPromises);
  res.json(loads);
});

//获取所有节点实例统计信息
app.get('/v1/servers/status', async (req, res) => {
  logRequest(req, 'REST请求');
  const statusPromises = matchmakers.map(async (url) => {
    try {
      console.log(`请求获取服务器${url}的实例统计信息`);
      const response = await axios.get(url + '/v1/server/status', { timeout: 30000 });
      console.log(`服务器${url}返回数据:`, response.data);
      return response.data;
    } catch (e) {
      const errorMessage = `无法获取${url}实例统计信息: ${e.message}`;
      console.log(errorMessage);
      return {
        serverUrl: url,
        error: errorMessage,
      };
    }
  });

  // 等待所有请求完成（无论成功还是失败）
  const statuses = await Promise.all(statusPromises);
  res.json(statuses);
});

const server = app.listen(config.HttpPort, () => {
  console.log('Dispatcher listening on *:' + config.HttpPort);
});

// 进程退出时清理资源
function cleanupResourcesOnExit() {
  console.log('正在关闭服务器...');
  // 关闭 HTTP 服务器
  server.close(() => {
    console.log('服务器已关闭');
  });

  process.exit(0); //终止当前 Node.js 进程
}

// 监听进程退出信号
process.on('SIGINT', cleanupResourcesOnExit);
process.on('SIGTERM', cleanupResourcesOnExit);
process.on('exit', () => {
  console.log('进程正常退出');
});
